package pl.coldwind.samlis.ag;

import java.util.Random;

/**
 * Simple mutation class for genetic algorythms.
 * @author Samlis Coldwind
 *
 */
public class Mutation {

	private static Random rand = new Random();
	
	/**
	 * Method to use mutation, build mainly for binary coding of chromosons.Mutation works depending on the possibility (from 0 to 10000).
	 * User can choose one of the mutation type. Also he needs to fill
	 * chromoson fill which is chain (this works for numeric coding only);
	 * @param chain - chromoson
	 * @param type - mutation type:
	 * 0 - one symbol for gen, one by one, toggle in gen, but can return the same value for each gen.
	 * 1 - one symbol for gen, one by one, toggle in gen, if it should happen it always changes the gen.
	 * 2 - decimal, treats the chain as gen, randomly adds or subtracts from the chain a random number between 0 and 10
	 * @param pos - possibility of mutation for 0 to 10000
	 * @param numeric - system of numbers (binary - 2, decimal - 10...)
	 * @return chromoson after mutation
	 */
	public static String mutate(String chain, int type, int pos,int numeric){
		
		switch(type){
			case 0:{
				return oneByOne(chain,numeric,pos);
			}
			case 1:{
				return oneByOneAlways(chain, numeric, pos);
			}
			case 2:{
				return decimalMutation(chain, pos);
			}
		}
		return null;
	}
	/**
	 * This is the one by one with possibility of returing the same gen
	 * @param chain chromoson chain
	 * @param numeric numeric system (Binary etc)
	 * @param pos possibility of mutation from 0 to 10000
	 * @return chain after mutation
	 */
	private static String oneByOne(String chain, int numeric, int pos){
		StringBuilder temp = new StringBuilder(chain);
		
		for(int i=0; i<chain.length(); i++){
			if(pos>rand.nextInt(10000)){
				temp.setCharAt(i, randomChar(numeric));
			}
		}
		
		return temp.toString();
	}
	/**
	 * Method that does mutation one by one, and if the mutation should happen, then it always toggles the
	 * value of gen.
	 * @param chain chromoson chain
	 * @param numeric numeric system (Binary etc)
	 * @param pos possibility of mutation from 0 to 10000
	 * @return chain after mutation
	 */
	private static String oneByOneAlways(String chain, int numeric, int pos){
		StringBuilder temp = new StringBuilder(chain);
		char tempChar;
		
		for(int i=0; i<chain.length(); i++){
			if(pos>rand.nextInt(10000)){
				tempChar = temp.charAt(i);
				do{
					temp.setCharAt(i, randomChar(numeric));
				}
				while(temp.charAt(i)==tempChar);
				
			}
		}
		
		return temp.toString();
	}
	
	/**
	 * Returns pseudo random char. The char is created in ascii code.
	 * It starts at ascii code 48 which is 0;
	 * @param numeric - numeric system
	 * @return random char starting from ascii code 48
	 */
	private static char randomChar(int numeric){
		return (char) (48+rand.nextInt(numeric));
	}
	
	/**
	 * Decimal simple mutation. Works by adding or subtracting int (from 0 to 10)
	 * from the main chain (which is converted from String into int)
	 * @param chain - chromoson to be mutated (actually its gen that should be, but as im mainly working on binary coding
	 * this one i call chromoson, knowing its not right)
	 * @param pos - possibility of mutation 
	 * @return mutated decimal chain
	 */
	private static String decimalMutation(String chain, int pos){
		int tempInt = Integer.parseInt(chain);
		
		if(pos>rand.nextInt(10000)){
			if(50>rand.nextInt(100)) tempInt -= rand.nextInt(10);
			else if(50<=rand.nextInt(100)) tempInt += rand.nextInt(10);
		}
		
		return tempInt+""; 
	}
}
